package com.seteam.collabcrossword;

import com.seteam.collabcrossword.types.LetterType;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import java.io.Serializable;

/**
 * This class represents a tile on the crossword board
 */
public class Tile implements Serializable {
    private static final long serialVersionUID = 3356678993647545798L;
    private char letter;
    private Coordinates coordinates;
    private boolean lock;
    private boolean acrossLock;
    private boolean downLock;
    private boolean shared;
    private boolean blocked;
    private Clue clue;
    private LetterType letterType;

    /**
     * Constructor
     * <p/>
     * Takes no parameters. Used to create an empty instance
     */
    public Tile() {
        //Provision to create an empty Tile
    }

    /**
     * Constructor
     *
     * @param coordinates the coordinates of the tile on the board
     */
    public Tile(final Coordinates coordinates) {
        this.coordinates = coordinates;
        init();
    }

    /**
     * Initializer for the fields
     */
    private void init() {
        this.letter = ' ';
        this.blocked = false;
        this.lock = false;
        this.shared = false;
        clue = null;
    }

    /**
     * Setter for shared
     *
     * @param shared boolean that tells whether the tile is shared or not
     *               <tt>TRUE</tt> if the tile is shared
     *               <tt>FALSE</tt> otherwise
     */
    public void setShared(final boolean shared) {
        this.shared = shared;
    }

    /**
     * Setter for blocked
     *
     * @param blocked boolean that tells whether the tile is blocked or not
     *                <tt>TRUE</tt> if the tile is blocked
     *                <tt>FALSE</tt> otherwise
     */
    public void setBlocked(final boolean blocked) {
        this.blocked = blocked;
    }

    /**
     * Sets the letterType
     *
     * @param letterType the type of letter that this tile will represent
     */
    public void setLetterType(final LetterType letterType) {
        this.letterType = letterType;
    }

    /**
     * Setter for clue
     *
     * @param clue the clue associated with this tile
     */
    public void setClue(final Clue clue) {
        if (letterType.equals(LetterType.START)) {
            this.clue = clue;
        }
    }

    /**
     * Setter for letter
     *
     * @param letter the letter that this tile should contain
     */
    public void setLetter(final char letter) {
        this.letter = letter;
    }

    /**
     * Setter for lock
     *
     * @param lock boolean that tells whether the tile is locked or not
     *             <tt>TRUE</tt> if the tile is locked
     *             <tt>FALSE</tt> otherwise
     */
    public void setLock(final boolean lock) {
        this.lock = lock;
    }

    /**
     * Getter for coordinates
     *
     * @return the coordinates of the tile
     */
    public Coordinates getCoordinates() {
        return coordinates;
    }

    /**
     * Getter for letter
     *
     * @return the letter that this tile contains
     */
    public char getLetter() {
        return letter;
    }

    /**
     * Getter for letterType
     *
     * @return the letter type
     */
    public LetterType getLetterType() {
        return letterType;
    }

    /**
     * Getter for shared
     *
     * @return <tt>TRUE</tt> if the tile is shared
     *         <tt>FALSE</tt> otherwise
     */
    public boolean isShared() {
        return shared;
    }

    /**
     * Getter for blocked
     *
     * @return <tt>TRUE</tt> if the tile is blocked
     *         <tt>FALSE</tt> otherwise
     */
    public boolean isBlocked() {
        return blocked;
    }

    /**
     * Getter for acrossLock
     *
     * @return tt>TRUE</tt> if the tile is locked across
     *         <tt>FALSE</tt> otherwise
     */
    public boolean isLockedAcross() {
        return acrossLock;
    }

    /**
     * Locks the tile for the word going across
     *
     * @param acrossLock boolean that indicates whether the tile has to be locked across or not
     *                   <tt>TRUE</tt> if the tile is has to be locked across
     *                   <tt>FALSE</tt> otherwise
     */
    public void lockAcross(final boolean acrossLock) {
        this.acrossLock = acrossLock;
    }


    /**
     * Getter for downLock
     *
     * @return tt>TRUE</tt> if the tile is locked across
     *         <tt>FALSE</tt> otherwise
     */
    public boolean isLockedDown() {
        return downLock;
    }

    /**
     * Locks the tile for the word going down
     *
     * @param downLock boolean that indicates whether the tile has to be locked down or not
     *                 <tt>TRUE</tt> if the tile is has to be locked down
     *                 <tt>FALSE</tt> otherwise
     */
    public void lockDown(final boolean downLock) {
        this.downLock = downLock;
    }

    /**
     * Calculates the hashcode based on various parameters
     *
     * @return the hashcode
     */
    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 31)
                .append(letter)
                .append(letterType)
                .append(coordinates.getxCoordinate())
                .append(coordinates.getyCoordinate())
                .toHashCode();
    }

    /**
     * Checks if this instance is equal to another instance that is passed
     * based on various parameters
     *
     * @param theObject the instance of {@link Tile} to be checked against
     * @return <tt>TRUE</tt> if this instance is equal to the instance passed
     *         <tt>FALSE</tt> otherwise
     */
    @Override
    public boolean equals(final Object theObject) {
        if (theObject == null) {
            return false;
        }

        if (theObject == this) {
            return true;
        }

        if (theObject.getClass() != getClass()) {
            return false;
        }

        final Tile theTile = (Tile) theObject;
        return new EqualsBuilder()
                .append(letter, theTile.getLetter())
                .append(letterType, theTile.getLetterType())
                .append(coordinates.getxCoordinate(), theTile.getCoordinates().getxCoordinate())
                .append(coordinates.getyCoordinate(), theTile.getCoordinates().getyCoordinate())
                .isEquals();
    }

    /**
     * Getter for clue
     *
     * @return the clue associated with this tile
     */
    public Clue getClue() {
        return clue;
    }

    /**
     * Getter for lock
     *
     * @return <tt>TRUE</tt> if the tile is locked
     *         <tt>FALSE</tt> otherwise
     */
    public boolean isLocked() {
        return lock;
    }
}
